﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using DeepEarth.Core;
using DeepEarth.Core.Controls;
using DeepEarth.Core.Utilities;
using DeepEarth.Map.Core.Utilities;

namespace DeepEarth.Map.Core
{
    public class Layer : Canvas, IList
    {
        #region  Dependency Properties

        #region Position Property

        public static readonly DependencyProperty PositionProperty = DependencyProperty.RegisterAttached("Position",
                 typeof(Location),
                 typeof(Layer),
                 new PropertyMetadata(PositionChanged));

        public static void SetPosition(DependencyObject obj, Location propertyValue)
        {
            obj.SetValue(PositionProperty, propertyValue);
        }

        public static Location GetPosition(DependencyObject obj)
        {
            return (Location)obj.GetValue(PositionProperty);
        }

        private static void PositionChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            // Only direct decendents of a Layer are positioned according to their Position/Location
            var layer = VisualTree.FindAncestor<ILayer>(sender as DependencyObject); //VisualTreeHelperGetParent(sender as DependencyObject) as Layer;

            //TODO: update rendering 
            // Handle location value changes
            // Beware: Static Layer not supported!
            if (args.NewValue != null && layer != null)
            {
                DynamicLayer dynamicLayer = null;

                if (layer is DynamicLayer)
                {
                    dynamicLayer = layer as DynamicLayer;
                }

                if (layer is LayerGroup)
                {
                    dynamicLayer = ((LayerGroup)layer).DynamicLayer;
                }

                var newLocation = (Location)args.NewValue;
                var weakListener = new WeakEventListener<DynamicLayer, Location>(dynamicLayer, newLocation);
                newLocation.PropertyChanged += weakListener.OnEvent;

                weakListener.DetatchAction = (listener, source) =>
                {
                    source.PropertyChanged -= listener.OnEvent;
                };

                var senderReference = new WeakReference(sender);
                weakListener.UnsafeEventAction = (listener, source, eventArgs) =>
                {
                    if (senderReference != null && senderReference.Target != null)
                    {
                        listener.SynchronizeElement(senderReference.Target as UIElement);
                    }
                };

                //something changed so synchronize now
                var element = sender as UIElement;
                if (element != null)
                {
                    dynamicLayer.SynchronizeElement(element);
                }
            }
        }

        #endregion

        #region PositionOrigin
        public static readonly DependencyProperty PositionOriginProperty = DependencyProperty.RegisterAttached("PositionOrigin",
            typeof(PositionOrigin),
            typeof(Layer),
            new PropertyMetadata(new PositionOrigin(0, 0)));

        public static void SetPositionOrigin(DependencyObject obj, PositionOrigin value)
        {
            obj.SetValue(PositionOriginProperty, value);
        }

        public static PositionOrigin GetPositionOrigin(DependencyObject obj)
        {
            return (PositionOrigin)obj.GetValue(PositionOriginProperty);
        }

        #endregion

        #endregion

        #region Properties
        public TemplateSelector TemplateSelector { get; set; }
        public bool UseVectorLayer { get; set; }

        public DynamicLayer DynamicLayer { get; set; }
        public VectorLayer VectorLayer { get; set; }

        #endregion

        public Layer()
        {
            Loaded += Layer_Loaded;
        }

        void Layer_Loaded(object sender, RoutedEventArgs e)
        {
            Loaded -= Layer_Loaded;
            foreach (var a in loadActions)
            {
                a();
            }

            loadActions.Clear();
            loadActions = null;
        }

        List<Action> loadActions = new List<Action>();

        Dictionary<object, UIElement> templatedItems = new Dictionary<object, UIElement>();        

        void ApplyTemplate(object item)
        {
            if (item is Layer)
            {
                AddToVisualTree(item as Layer);
                return;
            }

            if (item is TileLayer)
            {
                AddToVisualTree(item as TileLayer);
                return;
            }

            DataTemplate t = null;

            if (TemplateSelector != null)
            {
                t = TemplateSelector.FindTemplate<DataTemplate>(item);
            }

            if (t == null && item is UIElement)
            {
                var element = item as UIElement;
                AddToVisualTree(element);
            }

            else if (t != null)
            {
                var fe = MapHelper.CreateElement(t, item);
                templatedItems.Add(item, fe);
                if (fe != null)
                {
                    AddToVisualTree(fe);
                }
            }
        }

        void AddToVisualTree(UIElement element)
        {
            if (element is Layer)
            {
                Children.Add(element);
            }
            else if (element is TileLayer)
            {
                Children.Add(element);
            }

            else if (element is ISelfPositioning)
            {
                var mp = element as IMultiLocation;

                if (UseVectorLayer && mp != null)
                {
                    if (VectorLayer == null)
                    {
                        VectorLayer = new VectorLayer();
                        Children.Add(VectorLayer);
                    }

                    if (mp != null)
                    {
                        VectorLayer.Add(mp.GetBoundingBox(), element);
                    }
                }
                else
                {
                    var path = element as ISelfPositioning;

                    if (DynamicLayer == null)
                    {
                        DynamicLayer = new DynamicLayer();
                        Children.Add(DynamicLayer);
                    }

                    if (DynamicLayer.MapInstance != null)
                    {
                        path.LocationToPoint = DynamicLayer.MapInstance.LocationToViewportPoint;
                    }
                    else
                    {
                        loadActions.Add(() => { path.LocationToPoint = DynamicLayer.MapInstance.LocationToViewportPoint; });
                    }

                    DynamicLayer.Children.Add(element);
                }
            }
            else
            {
                if (DynamicLayer == null)
                {
                    DynamicLayer = new DynamicLayer();
                    Children.Add(DynamicLayer);
                }

                DynamicLayer.Children.Add(element);
            }
        }

        // Implements IList to facilitate adding elements in XAML
        #region IList
        public int Add(object value)
        {
            ApplyTemplate(value);

            return 0;
        }

        public void Clear()
        {
            templatedItems.Clear();

            if (DynamicLayer != null)
            {
                DynamicLayer.Clear();
                DynamicLayer = null;
            }

            if (VectorLayer != null)
            {
                VectorLayer.Clear();
                VectorLayer = null;
            }

            Children.Clear();
        }

        public bool Contains(object value)
        {
            var element = value as UIElement;
            return templatedItems.ContainsKey(value) || 
                (element != null &&
                    (Children.Contains(element) ||
                    (DynamicLayer != null && DynamicLayer.Children.Contains(element)) ||
                    (VectorLayer != null && VectorLayer.Children.Contains(element))));
        }

        public int IndexOf(object value)
        {
            throw new NotImplementedException();
        }

        public void Insert(int index, object value)
        {
            throw new NotImplementedException();
        }

        public bool IsFixedSize
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsReadOnly
        {
            get { throw new NotImplementedException(); }
        }

        public void Remove(object value)
        {
            var element = value as UIElement;

            if (templatedItems.ContainsKey(value))
            {
                element = templatedItems[value];
                templatedItems.Remove(value);
            }

            if (element != null && !Children.Remove(element))
            {
                bool removed = false;
                if (DynamicLayer != null)
                {
                    DynamicLayer.Children.Remove(element);
                    removed = true;
                }
                if (!removed && VectorLayer != null)
                {
                    VectorLayer.Children.Remove(element);
                }
            }

        }

        public void RemoveAt(int index)
        {
            throw new NotImplementedException();
        }

        public object this[int index]
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void CopyTo(Array array, int index)
        {
            throw new NotImplementedException();
        }

        public int Count
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsSynchronized
        {
            get { throw new NotImplementedException(); }
        }

        public object SyncRoot
        {
            get { throw new NotImplementedException(); }
        }

        public IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}